﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Runtime.Remoting.Messaging;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using GalaSoft.MvvmLight.Messaging;
using NLog;
using SellAvi.Classes;
using SellAvi.Properties;
using SellAvi.Services;


namespace SellAvi.ViewModel
{
    public class UploadProductViewModel : ViewModelBase
    {
        private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
        public static RelayCommand ReUploadProductCommand { get; set; }
        public static RelayCommand ReUploadSelectedProductCommand { get; set; }
        public static RelayCommand UploadProductCommand { get; set; }
        public static RelayCommand ForceUploadCommand { get; set; }
        public static RelayCommand UnMarkProductCommand { get; set; }
        public static RelayCommand UnMarkAllProductCommand { get; set; }
        public static RelayCommand NextProductCommand { get; set; }
        public static RelayCommand PrevProductCommand { get; set; }
        public static RelayCommand ClickTaskCommand { get; set; }
        public static RelayCommand<TabControl> TabChangedCommand { get; set; }
        public static RelayCommand<IList> SelectTasksCommand { get; set; }
        public WebClientEx WebClient { get; set; }

        private string _publicationStatusMessage;
        public string PublicationStatusMessage
        {
            get { return _publicationStatusMessage; }
            set
            {
                _publicationStatusMessage = value;
                RaisePropertyChanged("PublicationStatusMessage");
            }
        }

        public delegate void AsyncUploadProductCaller();

        public int TotalProductsCount { get { return TaskList.Count; } }

        public int CurrentProductNumber { get { return ProductViewer.CurrentPosition + 1; } }
        private IList _selectedTasks;

        public AvitoTask CurrentAvitoTask
        {
            set
            {
                var ap = ProductViewer.CurrentItem as AvitoTask;
                ap = value;
                RaisePropertyChanged("CurrentAvitoTask"); //?
            }
            get
            {
                //return TaskList[ProductViewer.CurrentPosition]; //BindingExpression
                var ap = ProductViewer.CurrentItem as AvitoTask;
                return ap;
            }
        }

        private bool _publicationInProgress = false;
        public bool PublicationInProgress
        {
            get { return _publicationInProgress; }
            set
            {
                _publicationInProgress = value;
                RaisePropertyChanged("PublicationInProgress");
            }
        }

        public int PublicationProgress
        {
            get
            {
                if (CurrentAvitoTask != null && CurrentAvitoTask.TaskStatus != null)
                    return (int)((double)CurrentAvitoTask.TaskStatus.OperationStepNumber / CurrentAvitoTask.TaskExecutionPlan.Count() * 100);
                return 0;
            }
        }
        public Visibility CurrentTaskHasErrors
        {
            get
            {
                return (CurrentAvitoTask != null && CurrentAvitoTask.TaskStatus != null && CurrentAvitoTask.TaskStatus.ErrorCode != null && CurrentAvitoTask.TaskStatus.ErrorCode != 0) ? Visibility.Visible : Visibility.Hidden;
            }
        }

        private IDataAccessService ServiceProxy { get; set; }
        public ICollectionView ProductViewer { get; set; }
        public ObservableCollection<AvitoTask> TaskList { get; set; }
        public DataGridStyleSelector DGStyleSelector { get; set; }


        public UploadProductViewModel(IDataAccessService servPxy)
        {
            ServiceProxy = servPxy;
            TaskList = new ObservableCollection<AvitoTask>();
            ProductViewer = CollectionViewSource.GetDefaultView(TaskList);

            NextProductCommand = new RelayCommand(NextProduct);
            PrevProductCommand = new RelayCommand(PrevProduct);
            ReUploadProductCommand = new RelayCommand(ReUploadProduct);
            ReUploadSelectedProductCommand = new RelayCommand(ReUploadSelectedProduct);
            UploadProductCommand = new RelayCommand(InitiatePublication);
            ForceUploadCommand = new RelayCommand(ForceUploadProduct);
            UnMarkProductCommand = new RelayCommand(UnMarkProduct);
            UnMarkAllProductCommand = new RelayCommand(UnMarkAllProduct);
            ClickTaskCommand = new RelayCommand(() => { /*TODO:*/});
            SelectTasksCommand = new RelayCommand<IList>(GridSelectionChanged);

            Messenger.Default.Register<WebClientEx>(this, (receivedClient) => { this.WebClient = receivedClient; });
            Messenger.Default.Register<List<AvitoTask>>(this, ReciveMarkedProduct);
            Messenger.Default.Register<NotificationMessage>(this, (nm) =>
            {
                if (nm.Notification == "FinishPublication")
                    ContinueAsyncPublication("FinishPublication");
                if (nm.Notification == "SendConfirmationCode")
                    ContinueAsyncPublication("SendConfirmationCode");
            });
        }

        private void ForceUploadProduct()
        {
            if (WebClient != null)
            {
                if (Settings.Default.CaptchaIsAutomatic)
                {
                    Settings.Default.PublicationSkipErrors = true;
                    PublicationInProgress = (PublicationInProgress != true);
                    UploadProduct();
                }
                else
                {
                    MessageBox.Show(
                        "Для безостановочной публикации необходимо включить автоматическое распознавание защитного кода в настройках программы",
                        "Автоматический режим не доступен", MessageBoxButton.OK, MessageBoxImage.Error);
                }
            }
            else
            {
                MessageBox.Show("Для выполнения операции необходима авторизация в программе!", "Требуется авторизация", MessageBoxButton.OK,
                    MessageBoxImage.Asterisk);
            }

        }

        private void InitiatePublication()
        {
            if (WebClient != null)
            {
                Settings.Default.PublicationSkipErrors = false;
                PublicationInProgress = (PublicationInProgress != true);
                UploadProduct();
            }
            else
            {
                MessageBox.Show("Для выполнения операции необходима авторизация в программе!", "Требуется авторизация", MessageBoxButton.OK,
                    MessageBoxImage.Asterisk);
            }

        }

        private void ReUploadSelectedProduct()
        {
            if (WebClient != null && !PublicationInProgress)
            {
                var avitoTask = _selectedTasks[0] as AvitoTask;
                if (avitoTask != null && _selectedTasks.Count == 1)
                {
                    ProductViewer.MoveCurrentTo(avitoTask);
                    RaisePropertyChanged("CurrentAvitoTask");
                    RaisePropertyChanged("CurrentProductNumber");
                    DGStyleSelector = new DataGridStyleSelector(CurrentAvitoTask);
                    RaisePropertyChanged("DGStyleSelector");

                    PublicationStatusMessage = "Товар отправлен на повторную публикацию";
                    avitoTask.TaskStatus = null;
                    PublicationInProgress = (PublicationInProgress != true);
                    UploadProduct();
                }
                else
                {
                    MessageBox.Show("Невозможно выполнить повторную публикацию нескольких товаров!", "Ошибка",
                        MessageBoxButton.OK, MessageBoxImage.Exclamation);
                }
            }

        }

        private void ReUploadProduct()
        {
            if (WebClient != null)
            {
                var avitoTask = ProductViewer.CurrentItem as AvitoTask;
                if (avitoTask != null && avitoTask.TaskStatus != null)
                {
                    var result = MessageBox.Show("Опубликовать товар повторно?", "Повторная публикация", MessageBoxButton.YesNo, MessageBoxImage.Question);
                    if (result == MessageBoxResult.Yes)
                    {
                        PublicationStatusMessage = "Товар отправлен на повторную публикацию";
                        avitoTask.TaskStatus = null;
                        PublicationInProgress = (PublicationInProgress != true);
                        UploadProduct();
                    }
                }
            }
            else
            {
                MessageBox.Show("Для выполнения операции необходима авторизация в программе!", "Требуется авторизация", MessageBoxButton.OK,
                    MessageBoxImage.Asterisk);
            }

        }

        //public delegate void OperationDelegate();
        /// <summary>
        /// URL:https://msdn.microsoft.com/en-us/library/2e08f6yc(v=vs.110).aspx
        /// Рекурсивный запуск для публикации каждого из товаров
        /// </summary>
        private void UploadProduct()
        {
            var avitoTask = ProductViewer.CurrentItem as AvitoTask;

            /*
             * avitoTask != null есть задача из списка
             * TaskStatus == null задача ранее не выполнялась
             * TaskDelay > 0 установлен таймер задержки
             * PublicationInProgress паралельно не запущено никаких задач
             */
            if (avitoTask != null && (avitoTask.TaskStatus == null || avitoTask.TaskDelay > 0) && PublicationInProgress)
            {
                Logger.Debug("Начато выполнение задачи: '{0}'", avitoTask.TaskName);
                PublicationStatusMessage = String.Empty;
                ContinueAsyncPublication();
            }
            else if (avitoTask != null && avitoTask.TaskStatus != null && TaskList.LastOrDefault() != avitoTask &&
                     PublicationInProgress)
            {
                PublishNextProduct();
            }
            else
            {
                StopPublication(new AvitoTaskStatus() { ErrorCode = 98 });
                MessageBox.Show("Выполнение списка задач завершено!", "Автоматическая публикация",
                    MessageBoxButton.OK,
                    MessageBoxImage.Asterisk);
            }



        }

        private void ContinueAsyncPublication(int publicationStep = 0)
        {
            PublicationInProgress = true;
            if (CurrentAvitoTask.TaskExecutionPlan.ElementAtOrDefault(publicationStep) != null)
            {
                CurrentAvitoTask.TaskExecutionPlan.ElementAt(publicationStep)
                    .BeginInvoke(new AsyncCallback(ProductPublicationNotification), null);
            }
            else
            {
                Logger.Warn("Попытка запуска несуществующего этапа выполняемой задачи #{0}", publicationStep);
            }

        }

        private void ContinueAsyncPublication(string stepName)
        {
            // вызывается из окна браузера при ручной публикации
            var avitoTask = ProductViewer.CurrentItem as AvitoTask;
            if (avitoTask != null)
            {
                avitoTask.TaskStatus.ErrorCode = null;
                avitoTask.TaskStatus.TryToResolveMethod = null;
            }
            var stepNum = CurrentAvitoTask.TaskExecutionPlan.FindIndex(x => x.Method.Name == stepName);
            ContinueAsyncPublication(stepNum);
        }


        public void ProductPublicationNotification(IAsyncResult result)
        {
            try
            {
                TaskProductUploader.OperationDelegate operationDelegate =
                    (TaskProductUploader.OperationDelegate)((AsyncResult)result).AsyncDelegate;
                AvitoTaskStatus returnValue = operationDelegate.EndInvoke(result);


                CurrentAvitoTask.TaskStatus = returnValue;
                PublicationStatusMessage = CurrentAvitoTask.TaskStatus.OperationMessage;
                RaisePropertyChanged("PublicationProgress");



                Logger.Debug("Операция {0} выполнена со статусом: {1}", operationDelegate.Method.Name,
                    returnValue.OperationSucceeded);

                CurrentAvitoTask.TaskStatus.OperationStepNumber = returnValue.OperationRepeatNeeded == false
                    ? (CurrentAvitoTask.TaskExecutionPlan.IndexOf(operationDelegate) + 1)
                    : CurrentAvitoTask.TaskExecutionPlan.IndexOf(operationDelegate);

                /*
                 * OperationRepeatNeeded имеет выше приоритет чем TryToResolveMethod
                 * OperationSucceeded имеет меньший приоритет, чем OperationRepeatNeeded
                 * TryToResolveMethod не вызывается из режима PublicationSkipErrors (кнопка >>)
                 */

                if (!PublicationInProgress)
                {
                    Logger.Info("Публикация прервана пользователем");
                    Application.Current.Dispatcher.Invoke(new Action(() => StopPublication(returnValue)));
                }
                //Если этап публикации пройден успешно, проходим на следующий, если он не оказался последним
                else if ((returnValue.OperationSucceeded || returnValue.OperationRepeatNeeded) &&
                         CurrentAvitoTask.TaskExecutionPlan.ElementAtOrDefault(CurrentAvitoTask.TaskStatus.OperationStepNumber) != null)
                {
                    ContinueAsyncPublication(CurrentAvitoTask.TaskStatus.OperationStepNumber);
                }
                //Если этап публикации пройден успешно и он был последним, то весь процесс публикации завершился успешно 
                else if (returnValue.OperationSucceeded &&
                         CurrentAvitoTask.TaskExecutionPlan.LastOrDefault() ==
                         CurrentAvitoTask.TaskExecutionPlan.ElementAtOrDefault(CurrentAvitoTask.TaskStatus.OperationStepNumber - 1))
                {
                    Logger.Info("Публикация успешно закончена, переходим к следующему товару");
                    Application.Current.Dispatcher.Invoke(
                        new Action(() => AfterPublicationSucceededActions(returnValue)));
                }
                // Обрабатываем только когда указан метод обработки и не требуется пропустить публикацию товара
                else if (returnValue.TryToResolveMethod != null && !Settings.Default.PublicationSkipErrors)
                {
                    //Запускаем метод разрешения нештатных ситуаций из главного потока приложения
                    Application.Current.Dispatcher.Invoke(new Action(() =>
                    {
                        StopPublication(returnValue);
                        returnValue.TryToResolveMethod();
                        //TODO: служит для обновления стутусов в DataGrid (переделать)
                        DGStyleSelector = new DataGridStyleSelector(CurrentAvitoTask);
                        RaisePropertyChanged("DGStyleSelector");
                    }));
                }
                else
                {
                    //Если не указан метод решения нештатной ситуации завершаем публикацию, переходим к следующему товару
                    Logger.Info("Пропуск публикации на шаге: ", CurrentAvitoTask.TaskStatus.OperationStepNumber);
                    Application.Current.Dispatcher.Invoke(
                        new Action(() => AfterPublicationFailedActions(returnValue)));
                }



            }
            catch (Exception ex)
            {
                Logger.Error(ex);
                //BUG: сообщение не будет выведено пользователю т.к. его надо присвоить CurrentAvitoTask.TaskStatus = AvitoTaskStatus... 
                Application.Current.Dispatcher.Invoke(new Action(() => AfterPublicationFailedActions(new AvitoTaskStatus() { ErrorCode = 99 })));
            }
            finally
            {
                //TODO: вероятно не надо запускать ContinueAsyncPublication, а приступать к публикации следующего товара
                //ContinueAsyncPublication(_currentPublicationStep);
            }

        }

        private void GridSelectionChanged(IList tasks)
        {
            _selectedTasks = tasks;
        }

        private void StopPublication(AvitoTaskStatus status)
        {
            Logger.Warn("Приостановка публикации на шаге {0} со статусом: {1}", status.OperationStepNumber, status.OperationMessage);
            PublicationStatusMessage = "Публикация приостановлена" + (status.ErrorCode == null ? String.Empty : String.Format(" (код: {0})", status.ErrorCode));
            RaisePropertyChanged("CurrentTaskHasErrors"); // кнопка "Повтор"
            PublicationInProgress = false;

        }

        private void AfterPublicationSucceededActions(AvitoTaskStatus status)
        {
            CurrentAvitoTask.TaskStatus.ErrorCode = 0; //BUG: NULL REF TaskStatus
            WebClient.BrowserNavigationNeeded(new Uri(CurrentAvitoTask.TaskCompletedUrl));
            PublishNextProduct();
        }
        private void AfterPublicationFailedActions(AvitoTaskStatus status)
        {
            PublicationStatusMessage = "Ошибка публикации";
            PublishNextProduct();
        }

        /// <summary>
        /// Публикация следующего товара, стоящего в очереди
        /// </summary>
        private void PublishNextProduct()
        {
            ServiceProxy.SaveContext();
            NextProduct();
            UploadProduct();
        }

        private void UnMarkAllProduct()
        {
            var result = MessageBox.Show(String.Format("Продолжить удаление ({0}) задач?", TaskList.Count), "Удаление", MessageBoxButton.YesNo, MessageBoxImage.Question);
            if (result == MessageBoxResult.Yes)
            {
                TaskList.Clear();
            }

            RaisePropertyChanged("CurrentProductNumber");
            RaisePropertyChanged("TotalProductsCount");
            RaisePropertyChanged("CurrentAvitoTask");
        }


        private void UnMarkProduct()
        {
            var result = MessageBox.Show(String.Format("Продолжить удаление ({0}) задач?", _selectedTasks.Count), "Удаление", MessageBoxButton.YesNo, MessageBoxImage.Question);
            if (result == MessageBoxResult.Yes)
            {
                var delList = _selectedTasks.Cast<AvitoTask>().ToArray();
                foreach (var item in delList)
                {
                    TaskList.Remove(item);
                }
            }

            RaisePropertyChanged("CurrentProductNumber");
            RaisePropertyChanged("TotalProductsCount");
            RaisePropertyChanged("CurrentAvitoTask");
        }
        private void UnMarkCurrentProduct(AvitoTask ap)
        {

            TaskList.Remove(ap);
            RaisePropertyChanged("CurrentProductNumber");
            RaisePropertyChanged("TotalProductsCount");
            RaisePropertyChanged("CurrentAvitoTask");
        }


        private void ReciveMarkedProduct(List<AvitoTask> tasks)
        {
            if (tasks == null || WebClient == null)
                return;
            foreach (var tk in tasks)
            {
                tk.WebClient = this.WebClient; //!!!
                TaskList.Add(tk);
            }

            ProductViewer.MoveCurrentToFirst();

            RaisePropertyChanged("CurrentProductNumber");
            RaisePropertyChanged("TotalProductsCount");
            RaisePropertyChanged("CurrentAvitoTask");

            DGStyleSelector = new DataGridStyleSelector(CurrentAvitoTask);
            RaisePropertyChanged("DGStyleSelector");

            MessageBox.Show(String.Format("Список задач пополнен на {0} шт.", tasks.Count), "Формирование списка задач", MessageBoxButton.OK, MessageBoxImage.Asterisk);

        }

        public void NextProduct()
        {
            if (!ProductViewer.MoveCurrentToNext())
            {
                ProductViewer.MoveCurrentToLast();
            }
            RaisePropertyChanged("CurrentAvitoTask");
            RaisePropertyChanged("CurrentProductNumber");

            DGStyleSelector = new DataGridStyleSelector(CurrentAvitoTask);
            RaisePropertyChanged("DGStyleSelector");
        }

        public void PrevProduct()
        {
            if (!ProductViewer.MoveCurrentToPrevious())
            {
                ProductViewer.MoveCurrentToFirst();

            }
            RaisePropertyChanged("CurrentAvitoTask");
            RaisePropertyChanged("CurrentProductNumber");

            DGStyleSelector = new DataGridStyleSelector(CurrentAvitoTask);
            RaisePropertyChanged("DGStyleSelector");
        }
    }
    public class DataGridStyleSelector : StyleSelector
    {
        private readonly AvitoTask _currentHighlightedTask;
        public Style DefaultStyle { get; set; }

        public DataGridStyleSelector(AvitoTask pr)
        {
            _currentHighlightedTask = pr;
        }

        private Style GetHighlightStyle()
        {
            Style style = new Style { TargetType = typeof(DataGridRow) };
            style.Setters.Add(new Setter(DataGridRow.BackgroundProperty, Brushes.Cornsilk));
            return style;
        }
        public override Style SelectStyle(object item, System.Windows.DependencyObject container)
        {
            var ap = item as AvitoTask;
            // Here you determine which style to use based on the property in your model
            if (ap == _currentHighlightedTask)
            {
                return GetHighlightStyle();
            }
            return DefaultStyle;

        }
    }
}
